package cate.game.pvp.ladder;

import cate.common.rpc.path.RpcLadderPath;
import cate.common.table.d.*;
import cate.common.util.GameResult;
import cate.game.GameBody;
import cate.game.framework.GameException;
import cate.game.play.part.FightSide;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.pvp.ladder.common.*;
import cate.game.pvp.ladder.msg.LadderDataResp;
import cate.game.pvp.ladder.msg.LadderOppoDetailResp;
import cate.game.res.MixRes;
import cate.game.role.Role;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.farm.PlayTimes;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.Position;
import com.google.common.collect.Lists;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static cate.common.table.d.GDLadder.MIN_REFRESH_DURATION;

@NoteClass("玩家身上的新天梯赛数据")
public class RoleLadder extends AbstractFarm {

    @NoteField("挑战次数")
    public PlayTimes playTimes;

    @NoteField("当前排名")
    public int rank;

    @NoteField("最近刷新时间")
    public long recentRefreshTime;

    @NoteField("当前刷新出来的对手列表")
    public List<LadderOppoSnapshot> opponents;

    @NoteField("玩家录像")
    public LinkedList<LadderReport> reports;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (Objects.isNull(playTimes)) {
            playTimes = new PlayTimes();
        }
        playTimes.initialize();

        if (Objects.isNull(opponents)) {
            opponents = Lists.newLinkedList();
        }

        if (Objects.isNull(reports)) {
            reports = Lists.newLinkedList();
        }
    }

    @Override
    public int getFuncId() {
        return GDFunc.LADDER_RACE;
    }

    @Override
    public int getFightFuncId() {
        return GDFunc.LADDER_RACE;
    }

    @Override
    public void onAfterOpen() {
        logger.info("玩家身上：天梯赛功能开启 onAfterOpen()");
        noticeUpdate();
        // 同布人物快照
        role.getRes().crossSync.syncSnapshot();
    }

    private void checkPosition() {
        if (!open) {
            return;
        }
        //主线阵容
        Position adventurePosition = role.getFight().getPosition(GDFunc.ADVENTURE);
        /*//进攻阵容
        Position position = role.getFight().getPosition(GDFunc.LADDER_RACE);
        if (position == null || position.grids.isEmpty()) {
            if (adventurePosition != null) {
                FuncPosition funcPosition = new FuncPosition();
                funcPosition.init();
                funcPosition.funcId = GDFunc.LADDER_RACE;
                funcPosition.positions.add(adventurePosition);
                role.getGame().fight.position.updatePosition(role, funcPosition);
            }
        }*/
        //防守阵容
        Position defPosition = role.getFight().getPosition(GDFunc.LADDER_RACE_DEF);
        if (defPosition == null || defPosition.grids.isEmpty()) {
            FuncPosition funcPosition = new FuncPosition();
            funcPosition.init();
            funcPosition.funcId = GDFunc.LADDER_RACE_DEF;
            funcPosition.positions.add(adventurePosition);
            role.getGame().fight.position.updatePosition(role, funcPosition);
        }
    }

    /** 刷新检查 */
    public void refreshCheck() throws GameException {
        if (Math.abs(System.currentTimeMillis() - this.recentRefreshTime) < MIN_REFRESH_DURATION) {
            throw new GameException().content("刷新速度太快,请稍后再试").s();
        }
        if (!canChallenge()) {
            //clearData();
            throw new GameException().content("当前天梯结算中，无法刷新").s();
        }
    }

    /** 天梯赛刷新 */
    public LadderRefreshResult refresh() throws GameException {
        LadderRefreshQuery query = new LadderRefreshQuery();
        query.roleId = role.getUid();
        query.num = 5;//暂时写死为5个
        LadderRefreshResult result = rpcSend().requestResponseCross(RpcLadderPath.REFRESH_G2C, query, LadderRefreshResult.class);
        if(result == null) {
            throw new GameException().content("天梯赛刷新失败").s();
        }
        //排序
        result.opponents = result.opponents
                .stream()
                .sorted(Comparator.comparingInt(LadderOppoSnapshot::getRank).reversed())
                .collect(Collectors.toList());
        this.opponents = result.opponents;
        this.recentRefreshTime = result.recentRefreshTime;
        syncSelfRank();
        return result;
    }

    /** 挑战前检查，如果不符合条件就抛出异常 */
    public void challengeCheck() throws GameException {
        if (!isOpen()) {
            throw new GameException().content("功能尚未开启").s();
        }
        if (isFighting()) {
            throw new GameException().content("当前正在战斗中").s();
        }

        if (!canChallenge()) {
            //clearData();
            throw new GameException().content("结算期间无法挑战").s();
        }

        if (CollectionUtils.isEmpty(role.getGame().table.ladder.base.getList())) {
            throw new GameException().content("table.ladder.base配置为空!");
        }
        if(!rankEnough()) {
            throw new GameException().content("竞技场未进入前100名").s();
        }
        GameResult<Void> r_playTimes = this.playTimes.available(role, getFuncId());
        if(!r_playTimes.ok()) {
            throw new GameException(r_playTimes).s();
        }
        Position adventurePosition = role.getFight().getPosition(GDFunc.ADVENTURE);
        if (adventurePosition == null) {
            throw new GameException().content("请先设置主线阵容").s();
        }
        //挑战前同步阵容
        checkPosition();
    }

    private boolean rankEnough() {
        int rank = role.getGame().pvp.pointRace.getHandler().findRank(role);
        return rank <= 100 && rank > 0;
    }

    private boolean canChallenge() {
        Date date = new Date();
        return LadderSeason.inSeason(date);
    }

    /** 从跨服同步自身的排名, 并返回 */
    public int syncSelfRank() {
        LadderLoadOppoPsResult r = role.getGame().pvp.ladder.getRankById(role.getUid());
        if(!r.ok()) {
            throw new GameException().content("从跨服同步自身的排名失败!").s();
        }
        this.rank = r.rank;
        return this.rank;
    }

    /** 满足执行前置条件后，真正执行挑战逻辑 */
    public void challengeDo(GameBody game, String opponentId) throws GameException {
        logger.info("天梯赛挑战 opponentId={}", opponentId);
        if(opponents == null || opponents.isEmpty()) {
            throw new GameException().content("缺少对手，请先刷新以生成对手!").s();
        }
        LadderOppoSnapshot opponent = this.opponents.stream()
                .filter(oppo -> oppo.id.equalsIgnoreCase(opponentId))
                .findFirst().orElse(null);
        if (Objects.isNull(opponent)) {
            throw new GameException().content("匹配对手失败，请重新刷新对手!").s();
        }

        //使用主线阵容，不单独修改
        EcResult<PlaySide> r_cps = role.getGame().fight.side
                .createPlaySideByRole(role, GDFunc.ADVENTURE, 1);
        if (!r_cps.ok()) {
            throw new GameException().content("天梯赛创建自身阵容失败!"+r_cps.message).s();
        }

        EcResult<PlaySide> r_cpsDef = role.getGame().fight.side
                .createPlaySideByRole(role, GDFunc.LADDER_RACE_DEF, 1);
        if (!r_cpsDef.ok()) {
            throw new GameException().content("天梯赛创建防守阵容失败!"+r_cpsDef.message).s();
        }
        PlaySide selfPs = r_cps.data;
        PlaySide selfPsDef = r_cpsDef.data;
        int selfRankCur = syncSelfRank();

        LadderLoadOppoPsResult r_lops = game.pvp.ladder.loadOpponentPlaySide(opponent.id, opponent.robot);
        if(r_lops == null || !r_lops.ok()) {
            throw new GameException().content("获取对手playSide失败"+"，请刷新后重试").s();
        }
        PlaySide oppoPs = r_lops.data;
        int oppoRankCur = r_lops.rank;

        // 向跨服申请锁定挑战对象
        LadderLockOppoResult r_lck = game.pvp.ladder.lockOpponent(new LadderLockOppoQuery(opponentId, role.getUid()));
        if(r_lck == null) {
            throw new GameException().content("锁定对手阵容失败"+"，请刷新后重试").s();
        }
        if(!r_lck.ok()) {
            throw new GameException().content("锁定对手阵容失败:"+r_lck.reason()+"，请刷新后重试").s();
        }

        LadderCompetition competition = new LadderCompetition(selfPs, selfRankCur, oppoPs, oppoRankCur,selfPsDef,opponent.robot);
        LadderFTC ftc = new LadderFTC(competition);
        ftc.funcId = getFightFuncId();
        ftc.setPositionApply(GDFunc.ADVENTURE);
        FightSide oppoFs = role.getGame().fight.side.createFightSideByPlaySide(GDFight.Team.B, oppoPs);
        EcResult<PlayPO> r_play = role.getGame().play.startByRole(role, ftc, oppoFs);

        if (!r_play.ok()) {
            throw new GameException().content(r_play.message);
        }
        this.playTimes.added();
        this.fighting = true;
        //天梯赛挑战次数记录
        role.getHistory().action.ladderChallange();
    }

    public void mopDo() throws GameException {
        String mopRwdStr = role.getGame().table.ladder.base.getList().get(0)
                .mopRwdStr;
        if (!StringUtils.hasLength(mopRwdStr)) {
            throw new GameException().content("配置错误!");
        }
        this.playTimes.added();
        //扫荡奖励
        MixRes reward = new MixRes(mopRwdStr);
        role.log.setOperation(GDOperation.LADDER_MOP_RWD);
        reward.add(role, true, GDOperation.LADDER_MOP_RWD);
        role.log.clearOperation();

        role.getGame().notice.dialogReward(role, reward);
        noticeUpdate();
        //天梯赛挑战次数记录
        role.getHistory().action.ladderChallange();
    }

    /**
     * 个人录像保存
     * @param report
     */
    public void addReport(LadderReport report) {
        //最近30条
        if (reports.size() >= GDLadder.REPLAY_SIZE_MAX_MINE) {
            reports.removeFirst();
        }
        this.reports.add(report);
    }

    public void noticeUpdate() {
        noticeUpdate(true);
    }

    public void noticeUpdate(boolean refresh) {
        if (isOpen()) {
            if (!canChallenge()){
                //clearData();
            } else {
                if (refresh) {
                    refresh();
                }
                LadderDataResp.doSend(role);
            }
        }
    }


    @Override
    public void onDaySpan(boolean silence) {
        super.onDaySpan(silence);
        //保留购买次数
        this.playTimes.reset();
        if (!silence) {
            noticeUpdate();
        }
    }

    /**
     * 更新防守阵容
     */
    public void updatePs(Role role) {
        EcResult<PlaySide> r_cps = role.getGame().fight.side
                .createPlaySideByRole(role, GDFunc.LADDER_RACE_DEF, 1);
        if (!r_cps.ok()) {
            logger.error("获取玩家 {} 天梯赛防守阵容失败: {}", role.getUid(), r_cps.message);
            return;
        }
        PlaySide defPS = r_cps.data;
        LadderPsQuery query = new LadderPsQuery();
        query.roleId = role.getUid();
        query.ps = defPS;
        try {
            role.getGame().pvp.ladder.updateLadderDefPs(query);
        } catch (Exception e) {
            logger.error("更新玩家 {} 天梯赛防守阵容失败:", role.getUid(), e);
        }
    }

    public void clearData() {
        this.playTimes.resetAll();
        this.reports.clear();
        this.opponents.clear();
        this.recentRefreshTime = 0L;
        if (rank > 0) {
            role.getHistory().action.ladderRankTimes(rank);
        }
        this.rank = 0;
    }

    @Override
    public void onEnter() {
        super.onEnter();
    }

    public void getOppoDetail(String opponentId, boolean robot, int srvId) {
        LadderLoadOppoPsResult r_oppoPs = role.getGame().pvp.ladder.loadOpponentPlaySide(opponentId, robot);
        if (r_oppoPs == null) {
            throw new GameException().content("天梯赛获取对手阵容失败!").s();
        }
        if (!r_oppoPs.ok()) {
            throw new GameException().content("天梯赛获取对手阵容失败!"+r_oppoPs.message).s();
        }
        String guildName = "";
        if (!robot) {
            LadderLockOppoQuery query = new LadderLockOppoQuery();
            query.oppoId = opponentId;
            OppoGuildResult result = role.getGame().rpcSend().requestResponseGame(RpcLadderPath.OPPO_GUILD_G2G, srvId, query,OppoGuildResult.class);
            if(result == null) {
                logger.error("获取对手公会名称失败:srvId = "+srvId+",query="+query);
                throw new GameException().content("获取对手公会名称失败"+"，请刷新后重试").s();
            }
            if(!result.ok()) {
                throw new GameException().content("获取对手公会名称失败:"+result.reason()+"，请刷新后重试").s();
            }
            guildName = result.guildName;
        }

        PlaySide oppoPs = r_oppoPs.data;
        LadderOppoDetailResp resp = new LadderOppoDetailResp();
        resp.playSide = oppoPs;
        resp.opponentId = opponentId;
        resp.guildName = guildName;
        resp.sendNow(role);
    }
}
